// 
//  fiveGame.js
//  白棋为玩家，黑棋子为电脑
//  
//  Created by jonz on 2017-10-15.
//  Copyright 2017 jonz. All rights reserved.
// 

//单例代理类，方式出现多个五子棋对象
var ProxySingletonGame = (function() {
	var instance;
	return function(dom, width, gridNumber, music, callback) {
		if(!instance) {
			instance = new FiveInRow(dom, width, gridNumber, music, callback);
		}
		return instance;
	}
})();

function FiveInRow(dom, width, geziNumber, music, callback) {
	this.dom = dom;
	this.width = width;
	this.geziNumber = geziNumber;
	this.maps = [];
	this.music = document.getElementById(music);
	this.canvas = document.getElementById(dom);
	this.canvas.width = width;
	this.canvas.height = width;
	this.grid = this.canvas.width / geziNumber;
	this.ctx = this.canvas.getContext('2d');
	this.isBai = true;
	this.turn = 1; //1为玩家下，0为电脑下
	this.aiMap = []; //权重坐标
	//刚下棋子的坐标点
	this.a = -1;
	this.b = -1;
	this.callback = callback;
	this.response = "ing..";
}

//下一个棋子，传入该棋子的坐标
FiveInRow.prototype.drawOneQizi = function(i, k) {
	this.ctx.beginPath();
	if(this.isBai) {
		this.maps[i][k] = 1;
		this.ctx.fillStyle = "#FFFFFF";
		this.isBai = false;
	} else {
		this.maps[i][k] = 2;
		this.ctx.fillStyle = "#000000";
		this.isBai = true;
	}
	this.a = i;
	this.b = k;
	this.ctx.arc(k * this.grid + this.grid / 2, i * 32 + this.grid / 2, this.grid / 2 - 2, 0, 2 * Math.PI);
	this.ctx.fill();
}

FiveInRow.prototype.clearMaps = function() {
	for(var i = 0; i < this.geziNumber; i++) { //初始化五子棋数组
		this.maps[i] = [];
		for(var k = 0; k < this.geziNumber; k++) {
			//						this.maps[i][k] =  parseInt(Math.random()*2+1);
			this.maps[i][k] = 0;
		}
	}
}

FiveInRow.prototype.restart=function(){
	this.clearMaps();
	this.response="ing...";
	this.turn=1;
}
/**五子棋AI
 *思路：对棋盘上的每一个空格进行估分，电脑优先在分值高的点落子
 * 棋型：
 * 〖五连〗只有五枚同色棋子在一条阳线或阴线上相邻成一排
 * 〖成五〗含有五枚同色棋子所形成的连，包括五连和长连。
 * 〖活四〗有两个点可以成五的四。
 * 〖冲四〗只有一个点可以成五的四。
 * 〖死四〗不能成五的四。
 * 〖三〗在一条阳线或阴线上连续相邻的5个点上只有三枚同色棋子的棋型。
 * 〖活三〗再走一着可以形成活四的三。
 * 〖连活三〗即：连的活三（同色棋子在一条阳线或阴线上相邻成一排的活三）。简称“连三”。
 * 〖跳活三〗中间隔有一个空点的活三。简称“跳三”。
 * 〖眠三〗再走一着可以形成冲四的三。
 * 〖死三〗不能成五的三。
 * 〖二〗在一条阳线或阴线上连续相邻的5个点上只有两枚同色棋子的棋型。
 * 〖活二〗再走一着可以形成活三的二。
 * 〖连活二〗即：连的活二（同色棋子在一条阳线或阴线上相邻成一排的活二）。简称“连二”。
 * 〖跳活二〗中间隔有一个空点的活二。简称“跳二”。
 * 〖大跳活二〗中间隔有两个空点的活二。简称“大跳二”。
 * 〖眠二〗再走一着可以形成眠三的二。
 * 〖死二〗不能成五的二。
 * 〖先手〗对方必须应答的着法，相对于先手而言，冲四称为“绝对先手”。
 * 〖三三〗一子落下同时形成两个活三。也称“双三”。
 * 〖四四〗一子落下同时形成两个冲四。也称“双四”。
 * 〖四三〗一子落下同时形成一个冲四和一个活三。
 * 分值表
 * 成5:100000分
 * 活4：10000分
 * 活3+冲4:5000分
 * 眠3+活2：2000分
 * 眠2+眠1:1分
 * 死棋即不能成5的是0分
 * @return {[type]} [description]
 */
FiveInRow.prototype.getPosition=function(){
    var a = new Array(2);
    var score = 0;
    for (var x = 0; x < this.geziNumber; x++) {
        for (var y = 0; y < this.geziNumber; y++) {
            if (this.maps[x][y] == 0) {
                if (this.judge(x, y) > score) {
                    score = this.judge(x, y);
                    a[0] = x;
                    a[1] = y;
                }
            }
        }
    }
    return a;
}
 
FiveInRow.prototype.AIplay=function(){
    var str = this.getPosition();
    // console.log("智能AI将在下面坐标下棋：" + str[0] + "," + str[1]);
    this.drawOneQizi(str[0],str[1]);
    this.turn=1;
    
}
 
FiveInRow.prototype.judge=function(x,y){
    var a = parseInt(this.leftRight(x, y, 1)) + parseInt(this.topBottom(x, y, 1)) + parseInt(this.rightBottom(x, y, 1)) + parseInt(this.rightTop(x, y, 1))+100; //判断白棋走该位置的得分
    var b = parseInt(this.leftRight(x, y, 2)) + parseInt(this.topBottom(x, y, 2)) + parseInt(this.rightBottom(x, y, 2)) + parseInt(this.rightTop(x, y, 2)); //判断黑棋走该位置的得分
// 	上下不验证
// var a = parseInt(this.leftRight(x, y, 1))  + parseInt(this.rightBottom(x, y, 1)) + parseInt(this.rightTop(x, y, 1))+100; //判断白棋走该位置的得分
//  var b = parseInt(this.leftRight(x, y, 2)) + parseInt(this.rightBottom(x, y, 2)) + parseInt(this.rightTop(x, y, 2)); //判断黑棋走该位置的得分
    var result = a + b;
    // console.log("我计算出了" + x + "," + y + "这个位置的得分为" + result);
    return result; //返回黑白棋下该位置的总和
}
 
FiveInRow.prototype.leftRight=function(x, y, num) {
    var death = 0; //0表示两边都没堵住,且可以成5，1表示一边堵住了，可以成5,2表示是死棋，不予考虑
    var live = 0;
    var count = 0;
//  var arr = new Array(this.geziNumber);
	var arr = [];
    for (var i = 0; i< this.geziNumber; i++) {
        arr[i] = [];
        for (var j = 0; j < this.geziNumber; j++) {
            arr[i][j] = this.maps[i][j];
        }
    }
    arr[x][y] = num;
    for (var i = x; i >= 0; i--) {
        if (arr[i][y] == num) {
            count++;
        } else if (arr[i][y] == 0) {
            live += 1; //空位标记
            i = -1;
        } else {
            death += 1; //颜色不同是标记一边被堵住
            i = -1;
        }
    }
    for (var i = x; i < this.geziNumber; i++) {
        if (arr[i][y] == num) {
            count++;
        } else if (arr[i][y] == 0) {
            live += 1; //空位标记
            i = 100;
        } else {
            death += 1;
            i = 100;
        }
    }
    count -= 1;
    // console.log(x + "," + y + "位置上的左右得分为" + model(count, death));
    return this.model(count, death);
}
 
FiveInRow.prototype.topBottom=function(x, y, num) {
    var death = 0; //0表示两边都没堵住,且可以成5，1表示一边堵住了，可以成5,2表示是死棋，不予考虑
    var live = 0;
    var count = 0;
    var arr = new Array(this.geziNumber);
    for (var i = 0; i< this.geziNumber; i++) {
        arr[i] = new Array(this.geziNumber);
        for (var j = 0; j < this.geziNumber; j++) {
            arr[i][j] = this.maps[i][j];
        }
    }
    arr[x][y] = num;
    for (var i = y; i >= 0; i--) {
        if (arr[x][i] == num) {
            count++;
        } else if (arr[x][i] == 0) {
            live += 1; //空位标记
            i = -1;
        } else {
            death += 1;
            i = -1;
        }
    }
    for (var i = y; i <= this.geziNumber; i++) {
        if (arr[x][i] == num) {
            count++;
        } else if (arr[x][i] == 0) {
            live += 1; //空位标记
            i = 100;
        } else {
            death += 1;
            i = 100;
        }
    }
    count -= 1;
    // console.log(x + "," + y + "位置上的上下斜得分为" + model(count, death));
    return this.model(count, death);
}
 
FiveInRow.prototype.rightBottom=function(x, y, num) {
    var death = 0; //0表示两边都没堵住,且可以成5，1表示一边堵住了，可以成5,2表示是死棋，不予考虑
    var live = 0;
    var count = 0;
    var arr = new Array(this.geziNumber);
    for (var i = 0; i< this.geziNumber; i++) {
        arr[i] = new Array(this.geziNumber);
        for (var j = 0; j < this.geziNumber; j++) {
            arr[i][j] = this.maps[i][j];
        }
    }
    arr[x][y] = num;
    for (var i = x, j = y; i >= 0 && j >= 0;) {
        if (arr[i][j] == num) {
            count++;
        } else if (arr[i][j] == 0) {
            live += 1; //空位标记
            i = -1;
        } else {
            death += 1;
            i = -1;
        }
        i--;
        j--;
    }
    for (var i = x, j = y; i < this.geziNumber && j < this.geziNumber;) {
        if (arr[i][j] == num) {
            count++;
        } else if (arr[i][j] == 0) {
            live += 1; //空位标记
            i = 100;
        } else {
            death += 1;
            i = 100;
        }
        i++;
        j++;
    }
    count -= 1;
    // console.log(x + "," + y + "位置上的右下斜得分为" + model(count, death));
    return this.model(count, death);
}
 
FiveInRow.prototype.rightTop=function(x, y, num) {
    var death = 0; //0表示两边都没堵住,且可以成5，1表示一边堵住了，可以成5,2表示是死棋，不予考虑
    var live = 0;
    var count = 0;
    var arr = new Array(this.geziNumber);
    for (var i = 0; i< this.geziNumber; i++) {
        arr[i] = new Array(this.geziNumber);
        for (var j = 0; j < this.geziNumber; j++) {
            arr[i][j] = this.maps[i][j];
        }
    }
    arr[x][y] = num;
    for (var i = x, j = y; i >= 0 && j <= this.geziNumber;) {
        if (arr[i][j] == num) {
            count++;
        } else if (arr[i][j] == 0) {
            live += 1; //空位标记
            i = -1;
        } else {
            death += 1;
            i = -1;
        }
        i--;
        j++;
    }
    for (var i = x, j = y; i < this.geziNumber && j >= 0;) {
        if (arr[i][j] == num) {
            count++;
        } else if (arr[i][j] == 0) {
            live += 1; //空位标记
            i = 100;
        } else {
            death += 1;
            i = 100;
        }
        i++;
        j--;
    }
    count -= 1;
    // console.log(x + "," + y + "位置上的右上斜得分为" + model(count, death));
    return this.model(count, death);
}
/**罗列相等效果的棋型(此处只考虑常见的情况，双成五，双活四等少概率事件不考虑)
 * 必胜棋：成五=活四==双活三=冲四+活三=双冲四
 * 
 * 
 * 
 */
FiveInRow.prototype.model=function(count, death) {
    // console.log("count" + count + "death" + death);
    var LEVEL_ONE = 0;//单子
    var LEVEL_TWO = 1;//眠2，眠1
    var LEVEL_THREE = 1500;//眠3，活2
    var LEVEL_FOER = 4000;//冲4，活3
    var LEVEL_FIVE = 10000;//活4
    var LEVEL_SIX = 100000;//成5
    if (count == 1 && death == 1) {
        return LEVEL_TWO; //眠1
    } else if (count == 2) {
        if (death == 0) {
            return LEVEL_THREE; //活2
        } else if (death == 1) {
            return LEVEL_TWO; //眠2
        } else {
            return LEVEL_ONE; //死棋
        }
    } else if (count == 3) {
        if (death == 0) {
            return LEVEL_FOER; //活3
        } else if (death == 1) {
            return LEVEL_THREE; //眠3
        } else {
            return LEVEL_ONE; //死棋
        }
    } else if (count == 4) {
        if (death == 0) {
            return LEVEL_FIVE; //活4
        } else if (death == 1) {
            return LEVEL_FOER; //冲4
        } else {
            return LEVEL_ONE; //死棋
        }
    } else if (count == 5) {
        return LEVEL_SIX; //成5
    }
    return LEVEL_ONE;
}


FiveInRow.prototype.ret = function() {
	if(this.turn === 0) {
		this.turn = 1;
	} else {
		this.turn = 0;
	}
}

FiveInRow.prototype.init = function() {
	this.clearMaps();
	this.drawQipan();
	this.drawQizi();
	var _ = this;
	//				初始化事件
	this.canvas.onclick = function() {
		//					this.music.load();
		//					this.music.play();
		if(!_.turn) {
			alert("该电脑下棋");
			return;
		}
		_.drawQipan();
		_.drawQizi();
		var x = parseInt(event.offsetX) - _.grid / 2;
		var y = parseInt(event.offsetY) - _.grid / 2;
		for(var i = 0; i < _.geziNumber; i++) {
			for(var k = 0; k < _.geziNumber; k++) {
				var c = Math.sqrt(Math.pow(x - _.grid * k, 2) + Math.pow(y - _.grid * i, 2));
				if(c < 10 && _.maps[i][k] == 0) {
					_.drawOneQizi(i, k);
					_.checkSuccess();
					_.ret();
					if(typeof _.response ==="string"){
						_.AIplay();
					}
					_.checkSuccess();
				}
			};
		};
		_.drawQipan();
		_.drawQizi();
		if(_.checkPingju()==0){
			_.success(0,0,0);
		}
	}
}

FiveInRow.prototype.drawQizi = function() {
	this.ctx.translate(this.grid / 2, this.grid / 2);
	this.ctx.shadowBlur = 0;
	this.ctx.shadowColor = "";
	for(var i = 0; i < this.maps.length; i++) {
		for(var k = 0; k < this.maps[i].length; k++) {
			if(i == this.a && k == this.b) {
				this.activeQizi(this.a, this.b);
			}
			if(this.maps[i][k] == 1) {
				this.ctx.beginPath();
				this.ctx.fillStyle = "#FFFFFF";
				this.ctx.arc(k * this.grid, i * this.grid, this.grid / 2 - 2, 0, 2 * Math.PI);
				this.ctx.fill();
			} else if(this.maps[i][k] == 2) {
				this.ctx.beginPath();
				this.ctx.fillStyle = "#000000";
				this.ctx.arc(k * this.grid, i * this.grid, this.grid / 2 - 2, 0, 2 * Math.PI);
				this.ctx.fill();
			}
			this.ctx.shadowBlur = 0;
			this.ctx.shadowColor = "";

		}
	}
	this.ctx.translate(-this.grid / 2, -this.grid / 2);
	this.ctx.shadowBlur = 10;
	this.ctx.shadowColor = "white";
}

FiveInRow.prototype.drawQipan = function() {
	this.ctx.clearRect(0,0,this.width,this.width);
	this.ctx.shadowBlur = 0;
	this.ctx.shadowColor = "";
	this.ctx.fillStyle = "#ff8c00";
	this.ctx.fillRect(0, 0, this.width, this.width);
	this.ctx.strokeStyle = "#000000";
	this.ctx.translate(this.grid / 2, this.grid / 2);
	for(var i = 0; i < this.geziNumber; i++) { //先画横线
		this.ctx.beginPath();
		this.ctx.moveTo(0, i * this.grid);
		this.ctx.lineTo(this.width - this.grid, i * this.grid);
		this.ctx.stroke();
	}
	for(var i = 0; i < this.geziNumber; i++) { //再画竖线
		this.ctx.beginPath();
		this.ctx.moveTo(i * this.grid, 0);
		this.ctx.lineTo(i * this.grid, this.width - this.grid);
		this.ctx.stroke();
	}
	this.ctx.translate(-this.grid / 2, -this.grid / 2);
	this.ctx.shadowBlur = 10;
	this.ctx.shadowColor = "white";
}

FiveInRow.prototype.activeQizi = function() {
	if(this.maps[this.a][this.b] == 1) {
		this.ctx.shadowBlur = 30;
		this.ctx.shadowColor = "#000000";
	} else if(this.maps[this.a][this.b] == 2) {
		this.ctx.shadowBlur = 30;
		this.ctx.shadowColor = "#FFFFFF";
	}
}

FiveInRow.prototype.acrossRule = function() {
	//横坐标
	var counthei = 0;
	var countbai = 0;
	var aihei = 0;
	var answer = false;
	for(var i = 0; i < 15; i++) {
		if(this.maps[this.a][i] == 1) {
			counthei = 0;
			countbai++;
			if(countbai >= 5) {
				this.success(countbai, counthei);
				answer = true;
			}
		} else if(this.maps[this.a][i] == 2) {
			countbai = 0;
			counthei++;
			aihei = counthei;
			if(counthei >= 5) {
				this.success(countbai, counthei);
				answer = true;
			}
		} else if(this.maps[this.a][i] == 0) {
			countbai = 0;
			counthei = 0;
		}
	}
	return {
		answer: answer
	};
}

FiveInRow.prototype.verticleRule = function() {
	//纵坐标
	var counthei = 0;
	var countbai = 0;
	var answer = false;
	for(var i = 0; i < this.geziNumber; i++) {
		if(this.maps[i][this.b] == 1) {
			counthei = 0;
			countbai++;
			if(countbai >= 5) {
				this.success(countbai, counthei);
				answer = true;
			}
		} else if(this.maps[i][this.b] == 2) {
			countbai = 0;
			counthei++;
			if(counthei >= 5) {
				this.success(countbai, counthei);
				answer = true;
			}
		} else if(this.maps[this.a][i] == 0) {
			countbai = 0;
			counthei = 0;
		}
	}
	return {
		answer: answer
	};
}

FiveInRow.prototype.declineRule = function() {
	//斜下
	var counthei = 0;
	var countbai = 0;
	var answer = false;
	var vy = this.a - this.b < 0 ? 0 : this.a - this.b;
	var vx = this.b - this.a < 0 ? 0 : this.b - this.a;
	for(var i = 0; vx < this.geziNumber && vy < this.geziNumber; vx++, vy++) {
		if(this.maps[vy][vx] == 1) {
			counthei = 0;
			countbai++;
			if(countbai >= 5) {
				this.success(countbai, counthei);
				answer = true;
			}
		} else if(this.maps[vy][vx] == 2) {
			countbai = 0;
			counthei++;
			if(counthei >= 5) {
				this.success(countbai, counthei);
				answer = true;
			}
		} else if(this.maps[this.a][i] == 0) {
			countbai = 0;
			counthei = 0;
		}
	}
	return {
		answer: answer
	};

}

FiveInRow.prototype.ascendingRule = function() {
	//斜上
	var counthei = 0;
	var countbai = 0;
	var answer = false;
	var vx = this.a + this.b > this.geziNumber ? this.geziNumber : this.a + this.b;
	var vy = this.a + this.b > this.geziNumber ? (this.a + this.b) - this.geziNumber : 0;
	for(var i = 0; vx > 0 && vy < this.geziNumber; vx--, vy++) {
		if(this.maps[vy][vx] == 1) {
			counthei = 0;
			countbai++;
			if(countbai >= 5) {
				this.success(countbai, counthei);
				answer = true;
			}
		} else if(this.maps[vy][vx] == 2) {
			countbai = 0;
			counthei++;
			if(counthei >= 5) {
				this.success(countbai, counthei);
				answer = true;
			}
		} else if(this.maps[this.a][i] == 0) {
			countbai = 0;
			counthei = 0;
		}
	}
	return {
		answer: answer
	};
}

FiveInRow.prototype.checkSuccess = function() {
	if(this.acrossRule().answer || this.verticleRule().answer || this.declineRule().answer || this.ascendingRule().answer) {
		if(typeof this.callback == "function") {
			this.callback(this.response);
		} else {
			throw "回调函数格式不对";
		}
	}
}

FiveInRow.prototype.checkPingju = function(){
	var space =0;		//空格
	for (var i = 0; i < this.maps.length; i++) {
		for (var j = 0; j < this.maps[i].length; j++) {
			if(this.maps[i][j]==0){
				space++;
			}
		}
	}
	return space;
}

FiveInRow.prototype.success = function(white, black,num) {
	if(white == 5) {
		this.response = 0;	//玩家胜利
	} else if(black == 5) {
		this.response = 1;	//电脑胜利
	} else if(num==0){
		this.response = 2;		//平局
	}
	this.callback(this.response);
}